Status — read this first. The CLI lives at
cli/benchspace_cli/but is not tracked in git and its.pysource is no longer present — only compiled.pycbytecode remains (cli/benchspace_cli/__pycache__/*.cpython-311.pyc). The backend RPC method it calls,benchspace.api.cli.list_dev_boxes, does not exist in the current control-plane source (benchspace/api/has nocli.py). Treat this page as documentation of an orphaned / work-in-progress prototype, reconstructed from the bytecode's embedded strings and docstrings — not from live source. Everything below is marked accordingly.
What it is
benchspace is a small command-line tool — "Codespaces for Frappe" — that lets a developer
manage hosted Dev Boxes for the current app + branch straight from their terminal, without
opening the dashboard. It is built with Typer (the CLI
framework) and rich for pretty console tables.
The defining idea is that a Dev Box is bound to a group derived from where you are: the
combination of the logged-in user, the git app, and the checked-out branch. Run a command
inside a repo on branch feature-x and the CLI talks only about the box for that
(owner, app, feature-x) slice — mirroring a parallel-worktree workflow.
Package layout (4 modules)
| Module | Role |
|---|---|
main.py |
Typer entry point — defines the commands (login, whoami, ls). |
api.py |
BenchspaceClient — thin token-auth REST client for the hosted Frappe site. |
config.py |
Read/write ~/.benchspace/config.toml (server URL + API token). |
context.py |
Resolve the {owner}:{app}:{branch} group from the current git working directory. |
Commands
All three are declared in main.py (Typer app, no_args_is_help=True).
| Command | What it does |
|---|---|
benchspace login |
Prompts for the server URL and API token (api_key:api_secret, input hidden), then saves them to ~/.benchspace/config.toml with chmod 600. |
benchspace whoami |
Prints the authenticated user (calls frappe.auth.get_logged_user) and the resolved group for the current directory (User: / App: / Branch: / Group:). If the group can't be resolved it prints Group: unavailable (<reason>). |
benchspace ls |
Lists the Dev Boxes in the current (owner, app, branch) group as a rich table with columns Slug · Status · Branch · Site URL. Prints No Dev Boxes for group <…> when empty. |
The bytecode shows the command function for the listing is registered as
ls; the embedded help text ("List the Dev Boxes in the current (owner, app, branch) group.") confirms its behavior.
Configuration — config.py
-
Default path:
~/.benchspace/config.toml. Override with theBENCHSPACE_CONFIGenv var (used by tests and to avoid clobbering a real prod config). -
File format:
[server] url = "https://boxes.buildwithhussain.com" token = "api_key:api_secret" -
read_config()raises a clean error when the file is missing (No benchspace config at <path>. Run \benchspace login` first.) or incomplete (Incomplete config at. Run `benchspace login` again.`). -
write_config()creates~/.benchspace/(parents=True, exist_ok=True), writes the TOML, and **chmod 600**s it. It rejects URL/token values containing quotes or newlines. -
Parsed with the stdlib
tomllib; modelled as a@dataclass Config(server, token).
API client — api.py
BenchspaceClient is a "thin REST client for the hosted benchspace Frappe site (token
auth)". It wraps a requests.Session with headers:
Authorization: token <token>
Accept: application/json
_call(method, dotted_path, params)→ issues a request to/api/method/<dotted_path>and raisesBenchspaceAPIErroron a transport failure (Could not reach <endpoint> from <…>) or a non-2xx response (surfacing the server'smessage/error details).whoami()→frappe.auth.get_logged_user.list_dev_boxes(group_key)→benchspace.api.cli.list_dev_boxes.
Group resolution — context.py
A group is identified by {owner}:{app}:{branch}:
- owner — the logged-in benchspace user, resolved from the API (not git).
- app — derived from the git remote / repo folder (
derive_app_namefromgit remote get-url originandgit rev-parse --show-toplevel). - branch — the currently checked-out branch (
git rev-parse --abbrev-ref HEAD).
Two domain errors give clean CLI messages instead of tracebacks:
| Exception | When | Message |
|---|---|---|
NotAGitRepoError |
cwd is not inside a git repo (or git not installed) |
not inside a git repository / git is not installed or not on PATH |
DetachedHeadError |
HEAD is detached (no branch to key the group on) | HEAD is detached — check out a named branch to use benchspace |
Typical session
benchspace login # save server URL + API token to ~/.benchspace/config.toml (600)
cd ~/code/my-frappe-app # a git repo on the branch you care about
benchspace whoami # User / App / Branch / Group
benchspace ls # Dev Boxes for this owner:app:branch group
Gaps & follow-ups
- Source (
.py) is missing and untracked — only.pycremain. Recover or re-commit the source. - The backend method
benchspace.api.cli.list_dev_boxesis not in the current control-plane; the CLI cannot function end-to-end until it is added (see page 06 — Task orchestration and page 07 — Frappe data model for where abenchspace/api/cli.pywould live). - No packaging metadata (
pyproject.toml/setup.py) was found undercli/, so install/entry-point wiring is unconfirmed.
See also: 01 Overview, 06 Task orchestration, 10 Dashboard (the GUI equivalent of these commands).